package psn.kiko.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import psn.kiko.annotation.sql.CommonFiledAutoSet;
import psn.kiko.annotation.sql.NeedCommonFiledAutoSet;
import psn.kiko.constant.CommonFiledConstant;
import psn.kiko.context.UserContext;
import psn.kiko.enumeration.DataBaseOperationType;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

/**
 * 公共字段字段设置切面
 */
@Aspect
@Component
@Slf4j
public class CommonFiledAutoSetAspect{
	/**
	 * 配置切点
	 */
	@Pointcut("@annotation(psn.kiko.annotation.sql.CommonFiledAutoSet)")
	public void commonFiledAutoSetPointCut(){}
	
	/**
	 * <p style="color:red;">Mapper接口抽象方法的形参的公共字段字段填充：默认取方法形参的首位，若有注解标注，则取注解标注的形参</p>
	 * @see CommonFiledAutoSet
	 * @see NeedCommonFiledAutoSet
	 * @param joinPoint 连接点
	 */
	@Before("commonFiledAutoSetPointCut()")
	public void commonFiledAutoSet(JoinPoint joinPoint){
		log.debug("执行公共字段自动设置...");
		//获取方法签名
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		
		//获取方法的CommonFiledAutoSet注解
		CommonFiledAutoSet annotation = signature.getMethod().getAnnotation(CommonFiledAutoSet.class);
		DataBaseOperationType dataBaseOperationType = annotation.value();
		
		//获取方法参数列表
		Object[] args = joinPoint.getArgs();
		//若方法无形参列表
		if(args == null||args.length == 0){return;}
		
		//默认情况下，需要公共字段填充的是方法的首个形参
		Object entity = args[0];
		
		//解除方法形参默认获取顺序的限制
		Parameter[] parameters = method.getParameters();
		for(int i = 0;i < parameters.length;i++){
			NeedCommonFiledAutoSet needCommonFiledAutoSet = parameters[i].getAnnotation(NeedCommonFiledAutoSet.class);
			// 若有NeedCommonFiledAutoSet注解标注了方法的形参
			if(needCommonFiledAutoSet != null){
				entity=args[i];
				break;
			}
		}
		
		//准备公共数据：日期时间、用户id
		String now = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss"));
		Integer userId = UserContext.getUserId();
		
		//形参的class对象：POJO类型或List类型，需要分别处理
		Class<?> entityClass = entity.getClass();
		
		//根据类型设置公共字段
		if(dataBaseOperationType == DataBaseOperationType.INSERT){
			log.info("执行单个实体插入的公共字段设置");
			//需要自动填充4个字段
			try{
				//创建字段
				Method setCreateAt = entityClass.getMethod(CommonFiledConstant.SET_CREATE_AT_METHOD,String.class);
				Method setCreateBy = entityClass.getMethod(CommonFiledConstant.SET_CREATE_BY_METHOD,Integer.class);
				//更新字段
				Method setUpdateAt = entityClass.getMethod(CommonFiledConstant.SET_UPDATE_AT_METHOD,String.class);
				Method setUpdateBy = entityClass.getMethod(CommonFiledConstant.SET_UPDATE_BY_METHOD,Integer.class);
				
				setCreateAt.invoke(entity,now);
				setCreateBy.invoke(entity,userId);
				setUpdateAt.invoke(entity,now);
				setUpdateBy.invoke(entity,userId);
				
			}catch(NoSuchMethodException|InvocationTargetException|IllegalAccessException e){
				log.error("单个插入时，设置公共字段出错：{}",e.getMessage());
				e.printStackTrace();
			}
			
		}else if(dataBaseOperationType == DataBaseOperationType.UPDATE){
			log.info("执行单个实体更新的公共字段设置");
			//需要自动填充2个字段
			try{
				//更新字段
				Method setUpdateAt = entityClass.getMethod(CommonFiledConstant.SET_UPDATE_AT_METHOD,String.class);
				Method setUpdateBy = entityClass.getMethod(CommonFiledConstant.SET_UPDATE_BY_METHOD,Integer.class);
				
				setUpdateAt.invoke(entity,now);
				setUpdateBy.invoke(entity,userId);
				
			}catch(NoSuchMethodException|InvocationTargetException|IllegalAccessException e){
				log.error("单个更新时，设置公共字段出错：{}",e.getMessage());
				e.printStackTrace();
			}
		}else if(dataBaseOperationType == DataBaseOperationType.INSERT_BATCH){
			log.info("执行多个实体插入的公共字段设置");
			//参数如果为集合类型，说明集合中的每个元素都需要进行公共字段的字段填充
			if(entity instanceof List){
				List list = (List) entity;
				if(list.size() > 0){
					try{
						//集合元素类型须为 实体对象
						Class<?> listElementClass = list.get(0).getClass();
						//创建字段
						Method setCreateAt = listElementClass.getMethod(CommonFiledConstant.SET_CREATE_AT_METHOD,String.class);
						Method setCreateBy = listElementClass.getMethod(CommonFiledConstant.SET_CREATE_BY_METHOD,Integer.class);
						//更新字段
						Method setUpdateAt = listElementClass.getMethod(CommonFiledConstant.SET_UPDATE_AT_METHOD,String.class);
						Method setUpdateBy = listElementClass.getMethod(CommonFiledConstant.SET_UPDATE_BY_METHOD,Integer.class);
						for(Object element: list){
							setCreateAt.invoke(element,now);
							setCreateBy.invoke(element,userId);
							setUpdateAt.invoke(element,now);
							setUpdateBy.invoke(element,userId);
						}
					}catch(Exception e){
						log.debug("批量插入时，设置公共字段出错，{}",e.getMessage());
						e.printStackTrace();
					}
				}
			}
			
		}else if(dataBaseOperationType == DataBaseOperationType.UPDATE_BATCH){
			log.info("执行单多实体更新的公共字段设置");
			//需要自动填充2个字段
			if(entity instanceof List){
				List list = (List) entity;
				if(list.size() > 0){
					try{
						Class<?> listElementClass = list.get(0).getClass();
						//更新字段
						Method setUpdateAt = listElementClass.getMethod(CommonFiledConstant.SET_UPDATE_AT_METHOD,String.class);
						Method setUpdateBy = listElementClass.getMethod(CommonFiledConstant.SET_UPDATE_BY_METHOD,Integer.class);
						for(Object element: list){
							setUpdateAt.invoke(element,now);
							setUpdateBy.invoke(element,userId);
						}
					}catch(Exception e){
						log.debug("批量更新时，设置公共字段出错，{}",e.getMessage());
						e.printStackTrace();
					}
				}
			}
			
		}
	}
}
